﻿using System;

namespace TrueSync.Physics2D
{
    public enum Category
    {
        BodyPosition,
        BodyHit,
        BodyHitNoCollideWithFlySubitem,
        Throw,
        Attack,
        FlySubitemAttack,
        CounterAttack,
        CounterFlySubitem,
        Status,
        GuardAlarm,
    }

    public class Fixture 
    {
        protected static int _fixtureIdCounter;
        
        internal CollisionWorld _world;

        internal Shape Shape;
        internal BroadphaseProxy Proxy;
        internal TSTransform localXf;// the body origin transform
        internal TSTransform Xf;
        
        internal ContactEdge ContactList;//record contact edge

        /// <summary>
        /// Fires when two shapes collide and a contact is created between them.
        /// Note that the first fixture argument is always the fixture that the delegate is subscribed to.
        /// </summary>
        public OnCollisionEventHandler OnCollision;

        public Fixture(Shape shape, TSVector2 position,  FP rotation, object userdata = null)
        {
            ID = _fixtureIdCounter++;
            Proxy = new BroadphaseProxy();
            localXf.p = position;
            localXf.q.Set(rotation);
            Shape = shape;
            UserData = userdata;
            Proxy.Fixture = this;
            Enabled = true;
        }

        public virtual void OnDestroy()
        {
            if (_world != null)
                _world.RemoveFixture(this); 
        }

        /// <summary>
        /// A unique id for this body.
        /// </summary>
        public int ID { get; protected set; }

        public object UserData { get; set; }

        public bool Enabled
        {
            get;set; 
        }

        public TSVector2 LocalPosition
        {
            get { return localXf.p; }
            set
            {
                SetTransform(value, LocalRotation);
            }
        }

        /// <summary>
        /// Get the angle in radians.
        /// </summary>
        /// <returns>Return the current world rotation angle in radians.</returns>
        public FP LocalRotation
        {
            get { return localXf.q.angle; }
            set
            {
                SetTransform(localXf.p, value);
            }
        }

        public void SetTransform(TSVector2 position, FP rotation)
        {
            localXf.q.Set(rotation);
            localXf.p = position;
        }

        public void Update(TSTransform parentTransform)
        {
            Xf = TSTransform.Mul(parentTransform , localXf);
            AABB aabb;
            Shape.ComputeAABB(out aabb, Xf);
            Proxy.AABB = aabb;
            if (_world != null)
                _world.UpdateAABB(this);
        }

        public void GetLocalTransform(out TSTransform transform)
        {
            transform = localXf;
        }

        public Category CollisionCategories
        {
            set;get;
        }

        /// <summary>
        /// Test a point for containment in this fixture.
        /// </summary>
        /// <param name="point">A point in world coordinates.</param>
        /// <returns></returns>
        public bool TestPoint(TSVector2 point)
        {
            return Shape.TestPoint(Xf, point);
        }

        /// <summary>
        /// Cast a ray against this Shape.
        /// </summary>
        /// <param name="output">The ray-cast results.</param>
        /// <param name="input">The ray-cast input parameters.</param>
        /// <param name="childIndex">Index of the child.</param>
        /// <returns></returns>
        public bool RayCast(out RayCastOutput output, RayCastInput input)
        {
            return Shape.RayCast(out output, input, Xf);
        }

        /// <summary>
        /// Get the fixture's AABB. This AABB may be enlarge and/or stale.
        /// If you need a more accurate AABB, compute it using the Shape and
        /// the body transform.
        /// </summary>
        /// <param name="aabb">The aabb.</param>
        /// <param name="childIndex">Index of the child.</param>
        public AABB GetAABB()
        {
            return Proxy.AABB;
        }

        public virtual void OnAddToWorld(CollisionWorld world)
        {

        }

        public virtual void OnRemoveFromWorld(CollisionWorld world)
        {

        }
    }
}


